//go:build homework

package homework

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"reflect"
	"strings"
)

var errInvalidEntity = errors.New("invalid entity")

func recursion(fd reflect.StructField, fdVal reflect.Value, fields []string, args []interface{}, fdNames map[string]bool) ([]string, []interface{}, map[string]bool) {
	if _, found := fdNames[fd.Name]; found {
		return fields, args, fdNames
	}
	fdNames[fd.Name] = true

	rootVal := fdVal
	fdTyp := fd.Type
	rootTyp := fd.Type

	for fdTyp.Kind() == reflect.Ptr {
		// 如果是指针，先取指针指向的类型和值
		fdTyp = fdTyp.Elem()
		fdVal = fdVal.Elem()
	}

	if !fdVal.IsValid() {
		//  It returns false if v is the zero Value.
		argV := reflect.New(fd.Type)
		args = append(args, argV.Elem().Interface())
		fields = append(fields, fmt.Sprintf("`%s`", fd.Name))
		return fields, args, fdNames
	}

	//sV, _ := fdVal.Interface().(sql.Scanner)
	dV, dOk := fdVal.Interface().(driver.Valuer)
	if dOk {
		// 如果实现了sql.Scanner类型 和 driver.Value 类型
		fdName := fmt.Sprintf("`%s`", fd.Name)
		dval, _ := dV.Value()

		cpV := reflect.New(fdTyp)
		methodScan := cpV.MethodByName("Scan")
		methodScan.Call([]reflect.Value{reflect.ValueOf(dval)})

		if rootTyp.Kind() == reflect.Ptr {
			args = append(args, cpV.Interface())
		} else {
			args = append(args, cpV.Elem().Interface())
		}
		fields = append(fields, fdName)

	} else if fdTyp.Kind() == reflect.Struct {
		//if fdTyp.Kind() == reflect.Struct {
		// 如果最后指针指向的是结构体

		// 如果是单个字段
		if !fd.Anonymous {
			args = append(args, fdVal.Interface())
			fields = append(fields, fmt.Sprintf("`%s`", fd.Name))
			return fields, args, fdNames
		}

		// 如果是匿名字段
		numField := fdTyp.NumField()
		for i := 0; i < numField; i++ {
			subFd := fdTyp.Field(i)
			var subFdVal reflect.Value
			if fdVal.IsValid() {
				subFdVal = fdVal.Field(i)
			} else {
				subFdVal = reflect.New(subFd.Type)
			}

			fields, args, fdNames = recursion(subFd, subFdVal, fields, args, fdNames)
			//fields = append(fields, rfields...)
			//values = append(values, rvalues...)
			//args = append(args, rargs...)
		}

	} else {
		fdName := fmt.Sprintf("`%s`", fd.Name)
		args = append(args, rootVal.Interface())
		fields = append(fields, fdName)
	}
	return fields, args, fdNames
}

// InsertStmt 作业里面我们这个只是生成 SQL，所以在处理 sql.NullString 之类的接口
// 只需要判断有没有实现 driver.Valuer 就可以了
func InsertStmt(entity interface{}) (string, []interface{}, error) {
	if entity == nil {
		return "", nil, errInvalidEntity
	}

	val := reflect.ValueOf(entity)
	typ := val.Type()
	// 检测 entity 是否符合我们的要求
	// 我们只支持有限的几种输入 (仅仅支持结构体 或者 指向结构体的一级指针)
	if typ.Kind() == reflect.Struct {
		// pass
	} else if typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct {
		// pass
	} else {
		return "", nil, errInvalidEntity
	}

	// 指针转换
	for typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
		val = val.Elem()
	}
	// 判断指针最后指向的是不是结构体
	if typ.Kind() != reflect.Struct {
		return "", nil, errInvalidEntity
	} else if typ.NumField() == 0 {
		// 结构体中没有字段
		return "", nil, errInvalidEntity
	}

	// 使用 strings.Builder 来拼接 字符串
	bd := strings.Builder{}

	// 构造 INSERT INTO XXX，XXX 是你的表名，这里我们直接用结构体名字
	fmt.Fprintf(&bd, "INSERT INTO `%s`", typ.Name())

	// 遍历所有的字段，构造出来的是 INSERT INTO XXX(col1, col2, col3)
	numField := typ.NumField()
	fields := make([]string, 0, numField)
	args := make([]interface{}, 0, numField)
	fdNames := make(map[string]bool, numField)

	// 递归取
	for i := 0; i < numField; i++ {
		fd := typ.Field(i)
		fdVal := val.Field(i)
		fields, args, fdNames = recursion(fd, fdVal, fields, args, fdNames)
	}

	// 在这个遍历的过程中，你就可以把参数构造出来
	//for i := 0; i < numField; i++ {
	//	fd := typ.Field(i)
	//	fdVal := val.Field(i)
	//	fdName := fmt.Sprintf("`%s`", fd.Name)
	//
	//	fields = append(fields, fdName)
	//	values = append(values, fdVal.Interface())
	//
	//	args = append(args, fdVal.Interface())
	//	//args = append(args, reflect.New(fd.Type).Interface())
	//}

	insertFields := strings.Join(fields, ",")
	fmt.Fprintf(&bd, "(%s)", insertFields)
	// 如果你打算支持组合，那么这里你要深入解析每一个组合的结构体
	// 并且层层深入进去

	// 拼接 VALUES，达成 INSERT INTO XXX(col1, col2, col3) VALUES
	valuesTemps := make([]string, 0, len(fields))
	for i := 0; i < len(fields); i++ {
		valuesTemps = append(valuesTemps, "?")
	}
	insertValues := strings.Join(valuesTemps, ",")
	fmt.Fprintf(&bd, " VALUES(%s);", insertValues)

	// 再一次遍历所有的字段，要拼接成 INSERT INTO XXX(col1, col2, col3) VALUES(?,?,?)
	// 注意，在第一次遍历的时候我们就已经拿到了参数的值，所以这里就是简单拼接 ?,?,?
	//args := make([]interface{}, 0, len(fields))
	//for i := 0; i < len(fields); i++ {
	//	value := values[i]
	//	append(args, reflect.TypeOf())
	//}

	return bd.String(), args, nil
}
